/*
 * keyboard.c
 *
 * Controle e gerenciamento das funcoes do Teclado
 *
 *  Created on: 01/09/2010
 *      Author: Wendell
 *
 * ======= O Teclado =======
 *
 * 		Um teclado é o meio mais comum de um usuário obter uma entrada no
 * computador, portanto é vital que você crie um driver de alguma espécie para
 * tratar e administrar o teclado.
 * 		Quando você dedicar-se para este, obter os básicos do telado não é tão
 * ruim. Aqui nós vamos mostrar os básicos: como saber se um tecla foi
 * pressionada, e como se converte o que é chamado de um  'scancode' para o
 * padrão de caracteres ASCII que nós podemos compreender apropriadamente.
 *
 * 		Um scancode é simplesmente o número de uma tecla. O teclado designa um
 * número para cada tecla no teclado; este é seu scancode. Os scancodes são
 * numerados geralmente de alto a baixo e esquerda para direita, com algumas
 * poucas exceções para layouts compatíveis com antigos teclados.
 * 		Você precisa usar uma tabela de pesquisa (um array de valores) e usar
 * o scancode como o índice desta tabela. A tabela de pesquisa é chamada de
 * keymap, e vai ser usada para traduzir os scancodes para valores ASCII mais
 * simples e indolor.
 * 		Uma última nota sobre um scancode antes nós entramos neste código para
 * ver se o bit 7 está atribuído (teste com 'scancode & 0x80'), então este é o
 * caminho do teclado contar-nos que uma tecla foi pressionada
 *
 * 		Para converter um scancode para um valor ASCII é só:
 *  meuchar = keymapBR[scancode];
 *
 *  	O teclado é anexado para o computador com um chip micro controlador especial
 *  na sua placa mãe. Este chip controlador do teclado tem 2 canais: um para o
 *  teclado, e um para o mouse. Também note que através deste chip controlador
 *  de teclado que você pode habilitar a linha de endereço A20 no processador
 *  que permite a você acessar memória passando da marca 1MByte (O GRUB habilita
 *  esse, você não precisa se preocupar com isso).
 *  	O controlador do teclado, sendo um dispositivo acessível pelo sistema,
 *  tem um endereço nas portas I/O que nós podemos usar para acessar e controlar.
 *  	O teclado possui 2 registradores principais: um registrador de Dados em
 *  0x60, e um registrador Controle em 0x64. Algo que o teclado deseja enviar ao
 *  computador é armazenado dentro do registrador de Dados.
 *  	O teclado irá levantar IRQ1 sempre que este possui dados para nós lermos.
 *  O teclado gerará um IRQ1 dizendo a nós que temos dados para pegar. O
 *  registrador de dados do teclado existe em 0x60. Quando o IRQ acontece, nós
 *  chamamos este tratamento que vai ler da porta 0x60.
 *  	Este dados que nós lemos são scancodes do teclado. Para este exemplo,
 *  nós checamos se um tecla foi pressionada ou realizada. Se foi justamente
 *  pressionada, nós traduzimos o scancode para ASCII, e escrevemos o caractere
 *  com uma linha. Escreva uma função 'keyboard_install' que chama
 *  'irqInstallHandler' para instalar o tratamento de teclado como de costume
 *  para 'keyboardHandler' em IRQ1.
 *
 */

#include <io.h>
#include <keyboard.h>

/**
 * keyboardBR eh o teclado do Brasil. Mais especificamente, do meu notebook.
 * Para converter um scancode para um valor ASCII é só:
 *  	meuchar = keymapBR[scancode];
 * Falta implementar acentos e teclas de controle (ALT, CTRL, ESC)
 */
unsigned char keymapBR[3][128] =  {
		//Mapa organizado de 10 em 10
		//Mapa regular
		{
				0,
				ESC, '1', '2', '3', '4', '5', '6', '7', '8', '9',          //1-10
				'0', '-', '=', BACKSPACE, TAB, 'q', 'w', 'e', 'r', 't',    //11-20
				'y', 'u', 'i', 'o', 'p', AGUDO, '[', ENTER, CTRL, 'a',     //21-30
				's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', CEDILHA, '~',      //31-40
				'\'', L_SHIFT, ']', 'z', 'x', 'c', 'v', 'b', 'n', 'm',       //41-50
				',', '.', ';', R_SHIFT, PRINTSCREEN, ALT, SPACE, CAPS, F1, F2, //51-60
				F3, F4, F5, F6, F7, F8, F9, F10, NUMLOCK, SCROLL_LOCK,     //61-70
				HOME, UP, PAGE_UP, '-', LEFT, 0 , RIGHT, '+', END, DOWN,    //71-80
				PAGE_DOWN, INSERT, DELETE, 0 , 0 , '\\', F11, F12,   //81-90
				0, // O resto estah indefinido
		},

		//Mapa shift
		{
				0,
				ESC, '!', '@', '#', '$', '%', '?', '&', '*', '(',
				')', '_', '+', BACKSPACE, TAB, 'Q', 'W', 'E', 'R', 'T',
				'Y', 'U', 'I', 'O', 'P', CRASE, '{', ENTER, CTRL, 'A',
				'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', MCEDILHA, '^',
				'"', L_SHIFT, '}', 'Z', 'X', 'C', 'V', 'B', 'N', 'M',
				'<', '>', ':', R_SHIFT, PRINTSCREEN, ALT, SPACE, CAPS, F1, F2,
				F3, F4, F5, F6, F7, F8, F9, F10, NUMLOCK, SCROLL_LOCK,
				HOME, UP, PAGE_UP, '-', LEFT, 0, RIGHT,	'+', END, DOWN,
				PAGE_DOWN, INSERT, DELETE, 0, 0, '|',  F11, F12,
				0,	// O resto estah indefinido
		},

		//Mapa alt
		{
			0,
			0, 0, 0, 0, EURO, 0, 0, 0, 0, 0,
			0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
			0, 0, 0, 0, 0, 0, A_ORD, ENTER, CTRL, 0,
			0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
			0, 0, O_ORD, 0, 0, 0, 0, 0, 0, 0,
			0, 0, '/', 0, 0, ALT, SPACE,
			0,	// O resto estah indefinido
		}
};

//flag para verificar se a tecla shift foi pressionada
uchar shift_state = false;
uchar alt_state = false;
unsigned escaped = 0;


// Dispara uma interupcao do teclado
void keyboardHandler(struct regs *r) {
	uchar scancode;

	// Le o buffer do teclado
	scancode = inportb(0x60);

	switch(scancode) {
	case L_SHIFT_IN: case R_SHIFT_IN:
	{
		shift_state = true;
		break;
	}
	case L_SHIFT_OUT: case R_SHIFT_OUT:
	{
		shift_state = false;
		break;
	}
	case ALT_IN:
	{
		alt_state = true;
	}
	case ALT_OUT:
	{
		alt_state = false;
	}
	case 0xe0:
	{
		escaped = 1;
		break;
	}
	default:
	{
		//Se o bit mais alto do byte que foi lido do teclado eh set,
		//entao uma tecla (de controle?) foi pressionada
		if (scancode & 0x80)
		{
			/**
			 * Este trecho serah usado para ver se o usuario pressionou
			 * SHIFT, ALT, ou teclas de controle
			 */
		}
		else
		{
			//Aqui, uma tecla comum foi pressionada.
			// Insere a nova tecla do buffer de entrada
			bufferInsert(&keyboardBuffer, keymapBR[shift_state][scancode]);

		}
		break;
	}//fim default
	}//fim switch

}


// Instala o keyboard handler no IRQ1
void keyboardInstall() {
	irqInstallHandler(1, keyboardHandler);

	initBuffer(&keyboardBuffer);
}
